package com.xg.mina;

import com.xg.mina.message.BasicHeader;
import com.xg.mina.message.Constant;
import com.xg.mina.message.MessageHeader;
import com.xg.mina.message.RtmpHeader;
import org.apache.commons.lang3.StringUtils;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;

public class ConnectCommand {
    private RtmpHeader rtmpHeader;
    private final String name = "connect";
    private final Double id = 1.0;
    private byte[] bytes;

    private ConnectProperty connectProperty;

    public RtmpHeader getRtmpHeader() {
        return rtmpHeader;
    }

    public void setRtmpHeader(RtmpHeader rtmpHeader) {
        this.rtmpHeader = rtmpHeader;
    }

    public String getName() {
        return name;
    }

    public Double getId() {
        return id;
    }

    public byte[] getBytes() {
        return bytes;
    }

    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }

    public ConnectProperty getConnectProperty() {
        return connectProperty;
    }

    public void setConnectProperty(ConnectProperty connectProperty) {
        this.connectProperty = connectProperty;
    }

    public static class ConnectProperty {
        /**
         * 客户端连接到的服务器端应用的名字
         */
        private String app;
        /**
         * Flash Player 版本号。和ApplicationScript getversion() 方法返回的是同一个字符串。
         */
        private String flashver;
        /**
         * 服务器 URL。具有以下格式：protocol://servername:port/appName/appInstance
         */
        private String tcUrl;
        /**
         *
         */
        private Double capabilities;
        /**
         * 如果使用了代理就是 true
         */
        private Boolean fpad;
        /**
         * 表明客户端所支持的音频编码
         */
        private Double audioCodecs;
        /**
         * 表明支持的视频编码。
         */
        private Double videoCodecs;
        /**
         * 表明所支持的特殊视频方法
         */
        private Double videoFunction;

        /**
         * 进行当前连接的 SWF 文件源地址
         */
        private String swfUrl;
        /**
         * SWF 文件所加载的网页 URL
         */
        private String pageUrl;
        /**
         * AMF 编码方法
         */
        private Double objectEncoding;

        public String getApp() {
            return app;
        }

        public void setApp(String app) {
            this.app = app;
        }

        public String getFlashver() {
            return flashver;
        }

        public void setFlashver(String flashver) {
            this.flashver = flashver;
        }

        public String getTcUrl() {
            return tcUrl;
        }

        public void setTcUrl(String tcUrl) {
            this.tcUrl = tcUrl;
        }

        public Double getCapabilities() {
            return capabilities;
        }

        public void setCapabilities(Double capabilities) {
            this.capabilities = capabilities;
        }

        public Boolean getFpad() {
            return fpad;
        }

        public void setFpad(Boolean fpad) {
            this.fpad = fpad;
        }

        public Double getAudioCodecs() {
            return audioCodecs;
        }

        public void setAudioCodecs(Double audioCodecs) {
            this.audioCodecs = audioCodecs;
        }

        public Double getVideoCodecs() {
            return videoCodecs;
        }

        public void setVideoCodecs(Double videoCodecs) {
            this.videoCodecs = videoCodecs;
        }

        public Double getVideoFunction() {
            return videoFunction;
        }

        public void setVideoFunction(Double videoFunction) {
            this.videoFunction = videoFunction;
        }

        public String getSwfUrl() {
            return swfUrl;
        }

        public void setSwfUrl(String swfUrl) {
            this.swfUrl = swfUrl;
        }

        public String getPageUrl() {
            return pageUrl;
        }

        public void setPageUrl(String pageUrl) {
            this.pageUrl = pageUrl;
        }

        public Double getObjectEncoding() {
            return objectEncoding;
        }

        public void setObjectEncoding(Double objectEncoding) {
            this.objectEncoding = objectEncoding;
        }

        @Override
        public String toString() {
            final StringBuffer sb = new StringBuffer("ConnectProperty{");
            sb.append("app='").append(app).append('\'');
            sb.append(", flashver='").append(flashver).append('\'');
            sb.append(", tcUrl='").append(tcUrl).append('\'');
            sb.append(", capabilities=").append(capabilities);
            sb.append(", fpad=").append(fpad);
            sb.append(", audioCodecs=").append(audioCodecs);
            sb.append(", videoCodecs=").append(videoCodecs);
            sb.append(", videoFunction=").append(videoFunction);
            sb.append(", swfUrl='").append(swfUrl).append('\'');
            sb.append(", pageUrl='").append(pageUrl).append('\'');
            sb.append(", objectEncoding=").append(objectEncoding);
            sb.append('}');
            return sb.toString();
        }
    }

    public IoBuffer calcBodyLength() {
        System.out.println("body开始===========");
        IoBuffer buffer = IoBuffer.allocate(1024);
        buffer.setAutoExpand(true);
        buffer.setAutoShrink(true);

        System.out.println();
        System.out.println(name);
        putValue(buffer, name);
        int pos = buffer.position();
        Utils.printIoBuffer(buffer);

        System.out.println();
        System.out.println(id);
        putValue(buffer, id);
        Utils.printIoBuffer(buffer, pos);
        pos = buffer.position();

        System.out.println("connect对象开始======================");
        buffer.put(Constant.amf0_object);
        Utils.printIoBuffer(buffer, pos);

        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getApp());
        putKeyByte(buffer, "app", connectProperty.getApp());
        Utils.printIoBuffer("app", buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getObjectEncoding());
        putKeyByte(buffer, "objectEncoding", connectProperty.getObjectEncoding());
        Utils.printIoBuffer("objectEncoding", buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getFpad());
        putKeyByte(buffer, "fpad", connectProperty.getFpad());
        Utils.printIoBuffer("fpad", buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getFlashver());
        putKeyByte(buffer, "flashVer", connectProperty.getFlashver());
        Utils.printIoBuffer("flashVer", buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getAudioCodecs());
        putKeyByte(buffer, "audioCodecs", connectProperty.getAudioCodecs());
        Utils.printIoBuffer("audioCodecs", buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getVideoFunction());
        putKeyByte(buffer, "videoFunction", connectProperty.getVideoFunction());
        Utils.printIoBuffer("videoFunction", buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getPageUrl());
        putKeyByte(buffer, "pageUrl", connectProperty.getPageUrl());
        Utils.printIoBuffer("pageUrl", buffer, pos);
        pos = buffer.position();

        System.out.println();
        String path = "livetv";
        System.out.println(path);
        putKeyByte(buffer, "path", path);
        Utils.printIoBuffer(buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getCapabilities());
        putKeyByte(buffer, "capabilities", connectProperty.getCapabilities());
        Utils.printIoBuffer(buffer, pos);
        pos = buffer.position();


        System.out.println();
        System.out.println(connectProperty.getSwfUrl());
        putKeyByte(buffer, "swfUrl", connectProperty.getSwfUrl());
        Utils.printIoBuffer(buffer, pos);
        pos = buffer.position();

        System.out.println();
        System.out.println(connectProperty.getVideoCodecs());
        putKeyByte(buffer, "videoCodecs", connectProperty.getVideoCodecs());
        Utils.printIoBuffer("videoCodecs", buffer, pos);
        pos = buffer.position();


        System.out.println();
        System.out.println(connectProperty.getTcUrl());
        putKeyByte(buffer, "tcUrl", connectProperty.getTcUrl());
        Utils.printIoBuffer(buffer, pos);
        pos = buffer.position();

        buffer.put(Constant.END_OF_OBJECT_SEQUENCE);
        Utils.printIoBuffer(buffer, pos);
        return buffer;
    }

    private void putKeyByte(IoBuffer buffer, String key, Object value) {
        int length = key.length();
        //key长度2个字节
        buffer.putShort((short) length);
        buffer.put(key.getBytes());
        putValue(buffer, value);
    }

    private void putValue(IoBuffer buffer, Object value) {
        if (value == null) {
            buffer.put(Constant.amf0_null);
            return;
        }
        if (value instanceof String) {
            String str = value.toString();
            buffer.put(Constant.amf0_string);
            buffer.putShort((short) str.length());
            buffer.put(str.getBytes());
            //System.out.println("字符串类型：" + Constant.amf0_string + "\t长度：" + str.length() + "\t内容：" + str);
        } else if (value instanceof Double) {
            Double number = (Double) value;
            buffer.put(Constant.amf0_number);
            buffer.putDouble(number);
            //System.out.println("数值类型：" + Constant.amf0_number + "\t内容：" + number);
        } else if (value instanceof Boolean) {
            boolean flag = (Boolean) value;
            buffer.put(Constant.amf0_boolean);
            if (flag) {
                buffer.put((byte) 1);
                //System.out.println("Boolean类型：" + Constant.amf0_boolean + "\t内容：" + 1);
            } else {
                buffer.put((byte) 0);
                //System.out.println("Boolean类型：" + Constant.amf0_boolean + "\t内容：" + 0);
            }
        } else {
            throw new RuntimeException("todo " + value.getClass());
        }
    }

    private void putKeyByte(IoBuffer buffer, Short key) {
        if (key != null) {
            buffer.putInt(key);
        }
    }

    public IoBuffer calc(IoSession session) {
        IoBuffer buffer = IoBuffer.allocate(1024);
        buffer.setAutoShrink(true);
        buffer.setAutoExpand(true);

        BasicHeader basicHeader = rtmpHeader.getBasicHeader();
        MessageHeader messageHeader = rtmpHeader.getMessageHeader();
        Byte fmt = basicHeader.getFmt();
        Integer fmtBytes = basicHeader.getFmtBytes();

        StringBuilder basicHeaderFirstByte = buildBasicFirstByte(fmt, fmtBytes);

        byte basicFmt = Utils.binaryByteToByte(basicHeaderFirstByte.toString());
        buffer.put(basicFmt);
        Integer messageStreamId = messageHeader.getMessageStreamId();
        if (fmtBytes == 0 || fmtBytes == 1) {
            if (fmtBytes == 0) {
                //基本头2个字节
                byte b = Utils.int1bytes(basicHeader.getChunkStreamId());
                System.out.println("基本头流ID：" + Integer.toHexString(b));
                buffer.put(b);
            } else {
                //基本头3个字节
                Integer chunkStreamId = basicHeader.getChunkStreamId();
                System.out.println("基本头流ID：" + Integer.toHexString(chunkStreamId));
                byte[] b = Utils.int2bytes(chunkStreamId);
                buffer.put(b);
            }
        }

        IoBuffer bodyBuffer = calcBodyLength();
        byte[] bodyBytes = Utils.bufferToBytes(bodyBuffer);
        messageHeader.setMessageLength(bodyBytes.length);

        byte messageTypeByte = messageHeader.getMessageType();
        switch (fmt) {
            case Constant.fmt00_11:
                messageHeader.setTimestamp(0);
                Integer timestamp = messageHeader.getTimestamp();
                buffer.put(Utils.int3bytes(messageHeader.getTimestamp()));
                buffer.put(Utils.int3bytes(messageHeader.getMessageLength()));
                buffer.put(messageTypeByte);
                buffer.putUnsignedInt(messageStreamId);
                if (timestamp > 0x00ffffff) {
                    //todo
                    System.out.println("重置消息");
                    throw new RuntimeException("TODO重置消息");
                }
                break;
            case Constant.fmt01_7:
                buffer.put(Utils.int3bytes(messageHeader.getTimestamp()));
                buffer.put(Utils.int3bytes(messageHeader.getMessageLength()));
                buffer.put(messageTypeByte);
                break;
            case Constant.fmt10_3:
                buffer.put(Utils.int3bytes(messageHeader.getTimestamp()));
                break;
            case Constant.fmt11_0:
                break;
            default:
                throw new RuntimeException("fmt值错误：" + fmt);
        }
        buffer.put(bodyBytes);
        return buffer;
    }

    public StringBuilder buildBasicFirstByte(Byte fmt, Integer fmtBytes) {
        StringBuilder basicHeaderFirstByte = new StringBuilder();
        //不足补2位
        String fmtStr = Utils.tobinaryString(fmt, 2);
        basicHeaderFirstByte.append(fmtStr);

        String fmtByteStr;
        if (fmtBytes == 0 || fmtBytes == 1) {
            if (fmtBytes == 0) {
                //消息头为2字节
                fmtByteStr = "000000";
            } else {
                //消息头为3字节
                fmtByteStr = "000001";
            }
        } else {
            //单字节，共6位
            fmtByteStr = StringUtils.leftPad(Integer.toBinaryString(fmtBytes), 6, '0');
        }
        basicHeaderFirstByte.append(fmtByteStr);
        return basicHeaderFirstByte;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer();
        sb.append('[');
        for (int i = 0; i < bytes.length; ++i)
            sb.append(i == 0 ? "" : ", ").append(bytes[i]);
        sb.append(']');
        return sb.toString();
    }
}
